﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Web.UI;
using System.ComponentModel;
using TA.DotNetUtils;
using TA.JSON;
using TA.Win.Utils.Design;
using System.Drawing.Design;

namespace TA.Turbo.Controls
{
	/// <summary>
	/// Describes the way entries in a <see cref="Repeater"/> are grouped when displayed on the client.
	/// </summary>
	public class RepeaterGrouping : IJsonCompatible
	{
		#region PUBLIC
		#region INIT
		/// <summary>
		/// Ctor.
		/// </summary>
		public RepeaterGrouping()
		{
			this.Enabled = true;
			this._header = new MultipleInstanceTemplate("GHeader");
			this._footer = new MultipleInstanceTemplate("GFooter");
			this._complete = new CombinedTemplate("Group", this._header, this._footer);
		}
		#endregion
		#region PROPS
		/// <summary>
		/// Gets or sets a value which indicates if this grouping will be used when displaying items.
		/// </summary>
		[DefaultValue(true)]
		public bool Enabled { get; set; }
		/// <summary>
		/// Gets or sets the comma-separated list of fields to be compared when grouping entries. 
		/// </summary>
		/// <remarks>
		/// This is only used on the client and has effect only if the <see cref="Comparer"/> event doesn't have a on the client side handler specified.
		/// </remarks>
		public string Fields
		{
			get
			{
				return this._mpvFields.Value;
			}
			set
			{
				this._mpvFields.Value = value;
			}
		}
		/// <summary>
		/// Gets or sets the order in which this grouping is applied to the entries.
		/// </summary>
		[DefaultValue(null)]
		public int? Order { get; set; }
		/// <summary>
		/// Gets the collection of fields to be compared when grouping entries. 
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", typeof(UITypeEditor))]
		public List<string> FieldList
		{
			get
			{
				return this._mpvFields.ValueList;
			}
		}
		/// <summary>
		/// The template to be used for each group's header.
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[Browsable(false)]
		public MultipleInstanceTemplate Header { get { return _header; } }
		/// <summary>
		/// The template to be used for each group's footer.
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[Browsable(false)]
		public MultipleInstanceTemplate Footer { get { return _footer; } }
		/// <summary>
		/// Gets the <see cref="CombinedTemplate"/> which is comprised of the <see cref="Header"/> and the <see cref="Footer"/> templates.
		/// </summary>
		[Browsable(false)]
		public CombinedTemplate Complete { get { return this._complete; } }
		#endregion
		#region EVENTS
		/// <summary>
		/// Raised on the client when <see cref="Repeater"/> needs to find the container element for this grouping.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public ClientEvent GetItemsContainer { get { return this._getItemsContainer; } }
		/// <summary>
		/// This event is raised when the control needs to parse the HTML of the rendered groups and add them to its internal array.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public ClientEvent ParseRenderedGroups { get { return this._parseRenderedGroups; } }
		/// <summary>
		/// Raised on the client or on the server when two entries need to be compared.
		/// </summary>
		/// <remarks>
		/// This event is always used (and <see cref="Fields"/> property ignored) on the server side. 
		/// On the client, this event "trumps" <see cref="Fields"/> property.
		/// </remarks>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public DualEvent<CompareEventArgs> Compare { get { return this._compare; } }
		/// <summary>
		/// Raised on the client or on the server when a group's header and/or footer need to be data-bound.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public DualEvent<RepeaterBindGroupEventArgs> Bind { get { return this._bind; } }
		#endregion
		#region METHODS
		///// <summary>
		///// Adds <see cref="Header"/>, <see cref="Footer"/>, <see cref="Compare"/>, <see cref="Bind"/> and <see cref="Fields"/> attributes.
		///// </summary>
		///// <param name="writer"></param>
		//public void AddAttributes(HtmlTextWriter writer)
		//{
		//  this.Header.AddAttributes(writer);
		//  this.Footer.AddAttributes(writer);

		//  writer.AddAttribute("G" + this._index + "Fields", this.Fields);

		//  this.ParseRenderedGroups.AddAttributes(writer);
		//  this.GetItemsContainer.AddAttributes(writer);
		//  this.Compare.AddAttributes(writer);
		//  this.Bind.AddAttributes(writer);
		//}
		/// <summary>
		/// Returns true if specified entries both fall into the same group; otherwise, false.
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <returns></returns>
		public bool EntriesEqual(object x, object y)
		{
			if (!this.Compare.Active)
				throw new InvalidOperationException("Grouping failed because no handlers were attached to the Compare event on the grouping.");

			CompareEventArgs cea = new CompareEventArgs(x, y);
			this.Compare.Raise(this, cea);
			return (cea.Result == 0);
		}
		/// <summary>
		/// Binds the specified group to this grouping's <see cref="Header"/> and <see cref="Footer"/>, for rendering.
		/// </summary>
		/// <param name="iGroup"></param>
		/// <param name="group"></param>
		public void DoBind(int iGroup, EntryGroup group)
		{
			if (!this.Compare.Active)
				throw new InvalidOperationException("Group rendering failed because no handlers were attached to the Bind event on the grouping.");

			RepeaterBindGroupEventArgs ea = new RepeaterBindGroupEventArgs(this.Header.Container, this.Footer.Container, group);
			this.Bind.Raise(this, ea);
		}
		#endregion
		#endregion
		#region INTERNAL
		#region VARS
		//int _index;
		//Repeater _repeater;
		MultiPartValue _mpvFields = new MultiPartValue();
		MultipleInstanceTemplate _header, _footer;
		CombinedTemplate _complete;
		DualEvent<CompareEventArgs> _compare = new DualEvent<CompareEventArgs>();
		DualEvent<RepeaterBindGroupEventArgs> _bind = new DualEvent<RepeaterBindGroupEventArgs>();
		ClientEvent _getItemsContainer = new ClientEvent(),
			_parseRenderedGroups = new ClientEvent();
		#endregion
		#region METHODS
		internal void Init(Repeater repeater, int index)
		{
			if (this.Order == null)
				this.Order = index;

			this.Header.Name = "G" + index;
			this.Header.Init();
			repeater.Controls.Add(this.Header.Container);

			this.Footer.Name = "G" + index;
			this.Footer.Init();
			repeater.Controls.Add(this.Footer.Container);

			this.Complete.Name = "G" + index;
		}
		#region JSON
		/// <summary>
		/// IsDirty.
		/// </summary>
		[Browsable(false)]
		public bool IsDirty
		{
			get { return true; }
		}
		/// <summary>
		/// Serialize.
		/// </summary>
		/// <param name="jw"></param>
		public void Serialize(JsonWriter jw)
		{
			this.SerializeObject(jw);
		}
		/// <summary>
		/// SerializeContents.
		/// </summary>
		/// <param name="jw"></param>
		public void SerializeContents(JsonWriter jw)
		{
			jw.PropertyName("E");
			jw.Value(this.Enabled);

			jw.PropertyName("F");
			jw.StartArray();
			foreach (string f in this.FieldList)
				jw.Value(f);
			jw.EndArray();

			jw.PropertyName("O");
			jw.Value(this.Order);

			if (!TPage.Current.IsPostBack)
			{
				jw.PropertyName("T");
				this._complete.Serialize(jw);

				jw.PropertyName("G");
				jw.Value(this.GetItemsContainer.Client);

				jw.PropertyName("P");
				jw.Value(this.ParseRenderedGroups.Client);

				if (!string.IsNullOrEmpty(this.Compare.Client))
				{
					jw.PropertyName("C");
					jw.Value(this.Compare.Client);
				}

				if (!string.IsNullOrEmpty(this.Bind.Client))
				{
					jw.PropertyName("B");
					jw.Value(this.Bind.Client);
				}
			}
		}
		/// <summary>
		/// Deserialize.
		/// </summary>
		/// <param name="jr"></param>
		public void Deserialize(JsonReader jr)
		{
			this.DeserializeObject(jr);
		}
		/// <summary>
		/// DeserializeContent.
		/// </summary>
		/// <param name="jr"></param>
		/// <param name="propShortName"></param>
		public void DeserializeContent(JsonReader jr, string propShortName)
		{
			switch (propShortName)
			{
				case "E": this.Enabled = (bool)jr.Value; break;
			}
		}
		#endregion
		#endregion
		#endregion

	}
}
